// 
// Entify project
//  
// Author:
//       Lauri Taimila <lauri@taimila.com>
// 
// Copyright (c) 2010 Lauri Taimila
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Collections.Generic;
using MonoDevelop.Core.Serialization;
using Cairo;
using System.Text.RegularExpressions;

namespace MonoDevelop.EntifyEditor
{
	/// <summary>
	/// EntityModel descibes one entity type. Entity code is generated from 
	/// these models.
	/// </summary>
	[DataItem("Entity")]
	public class Entity : IComparable
	{
		/// <summary>
		/// Constuctor for XML Serializer.
		/// </summary>
		private Entity()
		{
			this.Id = Guid.NewGuid().ToString();
			this.Name = "";
			this.RGB = new List<int>() { 0, 0, 0 };
			this.point = new List<double>() { 0, 0 };
			this.size = new List<double>() { 0, 0 };
			this.Description = "";
			this.Version = 1.0;
			this.Properties = new List<Property>();
			this.Relations = new List<Relation>();
		}

		/// <summary>
		/// Creates an new Entity instance.
		/// </summary>
		/// <param name="name">
		/// Name of the entity. <see cref="System.String"/>
		/// </param>
		public Entity(string name)
		{
			this.Id = Guid.NewGuid().ToString(); 
			this.Name = name;
			this.RGB = new List<int>() { 34366, 45641, 16066 };
			this.point = new List<double>() { 15.5, 15.5 };
			this.size = new List<double>() { 135, 180 };
			this.Description = "";
			this.Version = 1.0;
			this.Properties = new List<Property>();
			this.Relations = new List<Relation>();
		}

		/// <summary>
		/// Unique identifier for the entity. This is used only by editor.
		/// </summary>
		[ItemProperty]
		public string Id
		{
			get;
			set;
		}

		/// <summary>
		/// Name of the entity. This is used as a class name when generating
		/// the entity code.
		/// </summary>
		[ItemProperty]
		public string Name
		{
			get;
			set;
		}

		/// <summary>
		/// Description for entity type. This is displayed to the programmer
		/// as tooltip in MonoDevelop/VS IDE.
		/// </summary>
		[ItemProperty]
		public string Description
		{
			get;
			set;
		}

		/// <summary>
		/// Entity version number.
		/// </summary>
		[ItemProperty]
		public double Version
		{
			get;
			set;
		}

		/// <summary>
		/// List of the entity's properties.
		/// </summary>
		[ItemProperty]
		public List<Property> Properties
		{
			get;
			set;
		}
		
		/// <summary>
		/// List of the entity's relations.
		/// </summary>
		[ItemProperty]
		public List<Relation> Relations
		{
			get;
			set;
		}

		/// <summary>
		/// Top-left corner of the entity on canvas.
		/// </summary>
		[ItemProperty]
		private List<double> point;
		public PointD Point
		{
			get
			{
				return new PointD(this.point[0], this.point[1]);
			}
			set
			{
				this.point[0] = value.X;
				this.point[1] = value.Y;
			}
		}

		/// <summary>
		/// Width and height of the entity on canvas.
		/// </summary>
		[ItemProperty]
		private List<double> size;
		public PointD Size
		{
			get
			{
				return new PointD(this.size[0], this.size[1]);
			}
			set
			{
				this.size[0] = value.X;
				this.size[1] = value.Y;
			}
		}

		/// <summary>
		/// Color of the entity on the canvas. Color is represented
		/// as int array instead of Color object, because of object
		/// serialization.
		/// </summary>
		[ItemProperty]
		public List<int> RGB
		{
			get;
			set;
		}

		/// <summary>
		/// Gets entity color as Cairo color. Color brightness can be changed with parameter.
		/// </summary>
		/// <param name="lightness">
		/// Lightness change to base color. Value must be from -1 to 1. 0 means no change. <see cref="System.Double"/>
		/// </param>
		/// <returns>
		/// Color of the entity with given lightness change. <see cref="Cairo.Color"/>
		/// </returns>
		public Cairo.Color GetColor(double lightness)
		{
			if(lightness >= 0)
			{
				double red = this.RGB[0] / 65535.0 + lightness < 1 ? (this.RGB[0] / 65535.0 + lightness) : 1;
				double green = this.RGB[1] / 65535.0 + lightness < 1 ? (this.RGB[1] / 65535.0 + lightness) : 1;
				double blue = this.RGB[2] / 65535.0 + lightness < 1 ? (this.RGB[2] / 65535.0 + lightness) : 1;
				return new Cairo.Color(red, green, blue);
			}
			else
			{
				lightness = lightness * -1;
				double red = (this.RGB[0] / 65535.0) - lightness > 0 ? (this.RGB[0] / 65535.0 - lightness) : 0;
				double green = (this.RGB[1] / 65535.0) - lightness > 0 ? (this.RGB[1] / 65535.0 - lightness) : 0;
				double blue = (this.RGB[2] / 65535.0) - lightness > 0 ? (this.RGB[2] / 65535.0 - lightness) : 0;
				return new Cairo.Color(red, green, blue);
			}
		}

		/// <summary>
		/// Entities are ordered by entity names (alphabetically).
		/// </summary>
		public int CompareTo (object obj)
		{
			if(obj == null || obj.GetType() != typeof(Entity))
			{
				return -1;
			}
			else
			{
				return this.Name.CompareTo((obj as Entity).Name);
			}
		}

		/// <summary>
		/// Is entity valid for code generation.
		/// </summary>
		public bool IsValid
		{
			get
			{
				if(this.IsEntityNameValid())
				{
					foreach(Property p in this.Properties)
					{
						if(!p.IsValid)
						{
							return false;
						}
					}

					foreach(Relation r in this.Relations)
					{
						if(!r.IsValid)
						{
							return false;
						}
					}

					return true;
				}
				else
				{
					return false;
				}
			}
		}

		/// <summary>
		/// Is entity name valid to be class name?
		/// </summary>
		public bool IsEntityNameValid()
		{
			var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("C#");
			return provider.IsValidIdentifier(this.Name);
		}
	}
}
